/*	--- 策略 Strategy --- 

#	定义一系列的算法，把它们一个个封装起来，并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。

==============================================================

【策略模式：Strategy】，是指，定义【一组算法】，并把其封装到【一个对象中】。然后在运行时，可以灵活的使用【其中的一个算法】。

【策略模式】在【Java标准库】中【应用非常广泛】

我们以【排序】为例，看看如何【通过Arrays.sort()】实现【忽略大小写】排序： */
import java.util.Arrays;

public class Main {
	public static void main(String[] args) throws InterruptedException {
		String[] array = { "apple", "Pear", "Banana", "orange" };

		Arrays.sort(array, String::compareToIgnoreCase);
		System.out.println(Arrays.toString(array));
	}
}

/*
如果我们想【忽略大小写排序】，就【传入String::compareToIgnoreCase】，如果我们想【倒序排序】，就【传入(s1, s2) -> -s1.compareTo(s2)】 ，这个比较两个元素大小的算法就是策略。

我们【观察 Arrays.sort(T[] a, Comparator<? super T> c) 】这个排序方法，它在内部实现了【TimSort排序】，但是，【排序算法】在【比较两个元素大小】的时候，需要借助我们【传入的Comparator对象】，才能完成比较。

因此，这里的【策略】是指【比较两个元素大小】的策略，可以是【忽略大小写比较】，可以是【倒序比较】，也可以【根据字符串长度比较】。

------------

因此，【上述排序】使用到了【策略模式】，它实际上指，在一个方法中，【流程】是【确定】的，但是，【某些关键步骤】的算法依赖【调用方】传入的【策略】，这样，传入【不同的策略】，即可获得【不同的结果】，大大增强了【系统的灵活性】。 
	-- 多态...


如果我们自己实现【策略模式的排序】，用【冒泡法】编写如下： */
import java.util.*;

public class Main {
	public static void main(String[] args) throws InterruptedException {
		String[] array = { "apple", "Pear", "Banana", "orange" };

		sort(array, String::compareToIgnoreCase);
		System.out.println(Arrays.toString(array));
	}

	static <T> void sort(T[] a, Comparator<? super T> c) {
		for (int i=0; i<(a.length-1); i++) {
			for (int j=0; j<(a.length-1-i); j++) {
				if (c.compare(a[j], a[j+1]) > 0) { // 从小到大
					// 注意这里: 比较两个元素的大小依赖传入的策略
					T temp = a[j];
					a[j] = a[j+1];
					a[j+1] = temp;
				}
			}
		}
	}
}


/*
一个【完整的策略模式】要【定义策略】以及【使用策略的上下文】。

我们以【购物车结算】为例，假设网站针对【普通会员、Prime会员】有不同的折扣，同时【活动期间】还有一个【满100减20的活动】，这些就可以作为【策略实现】。

先定义【打折策略】接口： */
public interface DiscountStrategy {
	// 计算折扣额度
	BigDecimal getDiscount(BigDecimal total);
}


/*
接下来，就是实现【各种策略】。

【普通用户策略】如下： */
public class UserDiscountStrategy implements DiscountStrategy {

	public BigDecimal getDiscount(BigDecimal total) {
		// 普通会员打9折
		return total.multiply(new BigDecimal("0.1")).setScale(2, RoundingMode.DOWN);
	}
}

/*
满减策略如下： */
public class OverDiscountStrategy implements DiscountStrategy {

	public BigDecimal getDiscount(BigDecimal total) {
		// 满1000-20优惠
		return total.compareTo(BigDecimal.valueOf(100)) >= 0 ? BigDecimal.valueOf(20) : BigDecimal.ZERO;
	}
}

/*
最后，要应用策略，我们需要一个DiscountContext： */
public class DiscountContext {
	// 持有某个Strategy
	private DiscountStrategy strategy = new UserDiscountStrategy();

	// 允许客户端设置新策略
	public void setStrategy(DiscountStrategy strategy) {
		this.strategy = strategy;
	}

	public BigDecimal calculatePrice(BigDecimal total) {
		return total.subtract(this.strategy.getDiscount(total)).setScale(2);
	}
}

/*
【调用方】必须【首先】创建【一个DiscountContext】，并【指定】一个策略（或者使用默认策略），即可获得【折扣后的价格】： */
DiscountContext ctx = new DiscountContext();

// 默认使用【普通会员折扣
BigDecimal pay1 = ctx.calculatePrice(BigDecimal.valueOf(105));
System.out.println(pay1);

// 使用满减折扣
ctx.setStrategy(new OverDiscountStrategy());
BigDecimal pay2 = ctx.calculatePrice(BigDecimal.valueOf(105));
System.out.prnitln(pay2);

// 使用Prime会员折扣
ctx.setStrategy(new PrimeDiscountStrategy());
BigDecimal pay3 = ctx.calculatePrice(BigDecimal.valueOf(105));
System.out.println(pay3);



/*
上述完整的策略模式如下图所示：

┌───────────────┐      ┌─────────────────┐
│DiscountContext│─ ─ ─>│DiscountStrategy │
└───────────────┘      └─────────────────┘
                                ▲
                                │ ┌─────────────────────┐
                                ├─│UserDiscountStrategy │
                                │ └─────────────────────┘
                                │ ┌─────────────────────┐
                                ├─│PrimeDiscountStrategy│
                                │ └─────────────────────┘
                                │ ┌─────────────────────┐
                                └─│OverDiscountStrategy │
                                  └─────────────────────┘


【策略模式的核心思想】是在【一个计算方法】中把【容易变化的算法】抽出来作为【“策略”参数】传进去，从而使得【新增策略】不必修改【原有逻辑】。


==========================================================


#	练习

使用【策略模式】新增【一种策略】，允许在【满100减20】的基础上,对【Prime会员】再打【七折】。*/




/*==========================================================


#	小结

1. 策略模式是为了允许调用方选择一个算法，从而通过不同策略实现不同的计算结果。

2. 通过扩展策略，不必修改主逻辑，即可获得新策略的结果。


-----------

核心思路:

	┌───────────────┐      ┌─────────────────┐
	│   Function    │─ ─ ─>│Method_Strategy  │(Set)
	└───────────────┘      └─────────────────┘
	                                ▲
	                                │ ┌─────────────────────┐
	                                ├─│class Strategy1      │
	                                │ └─────────────────────┘
	                                │ ┌─────────────────────┐
	                                ├─│class Strategy2      │
	                                │ └─────────────────────┘
	                                │ ┌─────────────────────┐
	                                └─│class Strategy3      │
	                                  └─────────────────────┘

固定 业务处理_算法名 Method = deal(String xx, int a)

通过Set Target_Strategy(class) 来选择算法

	┌───────────────┐      ┌─────────────────┐
	│class Strategy │─ ─ ─>│Practical Method │
	└───────────────┘      └─────────────────┘
								

*/






















